D:\a\scloud-dns\scloud-dns\src\dns\q_name.rs
Line | Count | Source |
1 | | use crate::exceptions::SCloudException; |
2 | | |
3 | | /// Parse a DNS QNAME from a DNS message buffer. |
4 | | /// |
5 | | /// This function supports: |
6 | | /// - Standard DNS label encoding |
7 | | /// - Name compression using pointers (RFC 1035, section 4.1.4) |
8 | | /// |
9 | | /// # Arguments |
10 | | /// * `buf` - Full DNS message buffer |
11 | | /// * `pos` - Offset in the buffer where the QNAME starts |
12 | | /// |
13 | | /// # Returns |
14 | | /// * `(String, usize)` |
15 | | /// - Parsed domain name (e.g. "www.example.com") |
16 | | /// - Number of bytes consumed at the original position |
17 | | /// |
18 | | /// # Errors |
19 | | /// Returns an error if: |
20 | | /// - The buffer is too short |
21 | | /// - A label length is invalid |
22 | | /// - Compression pointers are malformed |
23 | | /// |
24 | | /// # Notes |
25 | | /// - When compression is used, the returned `usize` corresponds |
26 | | /// to the position right after the pointer (not the expanded name). |
27 | | /// - The caller is responsible for passing the correct initial offset |
28 | | /// (e.g. 12 for the first QNAME in a DNS packet). |
29 | 22 | pub(crate) fn parse_qname(buf: &[u8], mut pos: usize) -> Result<(String, usize), SCloudException> { |
30 | 22 | let mut labels = Vec::new(); |
31 | 22 | let mut _jumped = false; |
32 | 22 | let mut end_pos = pos; |
33 | | |
34 | | loop { |
35 | 59 | if pos >= buf.len() { |
36 | 2 | return Err(SCloudException::SCLOUD_IMPOSSIBLE_PARSE_QNAME_POS_GREATER_THAN_BUF); |
37 | 57 | } |
38 | | |
39 | 57 | let len = buf[pos]; |
40 | | |
41 | | // Compression 0xC0xx |
42 | 57 | if len & 0xC0 == 0xC0 { |
43 | 3 | if pos + 1 >= buf.len() { |
44 | 1 | return Err(SCloudException::SCLOUD_IMPOSSIBLE_PARSE_QNAME); |
45 | 2 | } |
46 | | |
47 | 2 | let offset = (((len as u16 & 0x3F) << 8) | buf[pos + 1] as u16) as usize; |
48 | | |
49 | 2 | if !_jumped { |
50 | 2 | end_pos = pos + 2; |
51 | 2 | }0 |
52 | | |
53 | 2 | _jumped = true; |
54 | | |
55 | 2 | let (name, _) = parse_qname(buf, offset)?0 ; |
56 | 5 | labels2 .extend2 (name.split('.')2 .map2 (|s| s.to_string())); |
57 | 2 | break; |
58 | 54 | } |
59 | | |
60 | 54 | if len == 0 { |
61 | 16 | if !_jumped { |
62 | 16 | end_pos = pos + 1; |
63 | 16 | }0 |
64 | 16 | break; |
65 | 38 | } |
66 | | |
67 | 38 | pos += 1; |
68 | | |
69 | 38 | if pos + len as usize > buf.len() { |
70 | 1 | return Err( |
71 | 1 | SCloudException::SCLOUD_IMPOSSIBLE_PARSE_QNAME_POS_AND_LEN_GREATER_THAN_BUF, |
72 | 1 | ); |
73 | 37 | } |
74 | | |
75 | 37 | let label = &buf[pos..pos + len as usize]; |
76 | 37 | let s = String::from_utf8(label.to_vec()) |
77 | 37 | .map_err(|_| SCloudException::SCLOUD_IMPOSSIBLE_PARSE_QNAME)?0 ; |
78 | | |
79 | 37 | labels.push(s); |
80 | 37 | pos += len as usize; |
81 | | } |
82 | | |
83 | 18 | Ok((labels.join("."), end_pos)) |
84 | 22 | } |
85 | | |
86 | | /// Parse a DNS QNAME at a specific offset and return only the name. |
87 | | /// |
88 | | /// This is mainly used internally when resolving compression pointers. |
89 | | #[allow(unused)] |
90 | 2 | pub(crate) fn parse_qname_at(buf: &[u8], offset: usize) -> Result<String, SCloudException> { |
91 | 2 | let (name, _consumed) = parse_qname(&buf, offset)?0 ; |
92 | 2 | Ok(name) |
93 | 2 | } |